﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using System.Runtime.Serialization;

namespace Sencha.Direct
{
	public class DirectJsonResult : JsonResult
	{
		private Type m_dataType;

		internal bool IsJsonP { get; set; }
		internal string CallbackFunctionName { get; set; }

		public virtual Type DataType { get { if (m_dataType != null) return m_dataType; return this.Data != null ? this.Data.GetType() : typeof(object); } set { m_dataType = value; } }
		internal XmlObjectSerializer Serializer { get; set; }
		internal bool EnableCompression { get; set; }

		public DirectJsonResult() { }
		public DirectJsonResult(object data, Type dataType)
		{
			base.Data = data;
			m_dataType = dataType;
		}

		public override void ExecuteResult(ControllerContext context)
		{
			if (context == null)
				throw new ArgumentNullException("context");
			HttpResponseBase response = context.HttpContext.Response;

			var callback = this.CallbackFunctionName;
			var isJSONP = this.IsJsonP && !string.IsNullOrEmpty(callback);

			if (!string.IsNullOrEmpty(this.ContentType))
				response.ContentType = this.ContentType;
			else if (!isJSONP)
				response.ContentType = "application/json";
			else
				response.ContentType = "text/javascript";

			if (this.ContentEncoding != null)
				response.ContentEncoding = this.ContentEncoding;

			if (this.EnableCompression)
				this.ApplyCompression(context.HttpContext.Response, context.HttpContext.Request);

			if (isJSONP)
			{
				response.Write(callback);
				response.Write("(");
			}

			if (this.Data != null)
			{
				var serializer = this.Serializer ?? new Sencha.Serialization.JsonSerializer(m_dataType);

				serializer.WriteObject(response.OutputStream, this.Data);
			}
			else
				response.Output.Write("null");

			if (isJSONP)
				response.Write(")");
		}

		private void ApplyCompression(HttpResponseBase resp, HttpRequestBase req)
		{
			var acceptEncoding = req.Headers["Accept-Encoding"];
			var range = req.Headers["Range"];
			var prevUncompressedStream = resp.Filter;

			if (string.IsNullOrEmpty(acceptEncoding) || !string.IsNullOrEmpty(range))
				return;

			if (acceptEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) != -1 || acceptEncoding == "*")
			{
				// gzip
				resp.Filter = new System.IO.Compression.GZipStream(prevUncompressedStream,
					System.IO.Compression.CompressionMode.Compress);
				resp.AppendHeader("Content-Encoding", "gzip");
			}
			else if (acceptEncoding.IndexOf("deflate", StringComparison.OrdinalIgnoreCase) != -1)
			{
				// defalte
				resp.Filter = new System.IO.Compression.DeflateStream(prevUncompressedStream,
					System.IO.Compression.CompressionMode.Compress);
				resp.AppendHeader("Content-Encoding", "deflate");
			}
		}

		public static bool IsCompressionEnabled(object responseResult)
		{
			if (responseResult is DirectJsonResult)
				return (responseResult as DirectJsonResult).EnableCompression;
			else
				return false;
		}
	}

	public class DirectJsonResult<T> : DirectJsonResult
	{
		new T Data { get { return (T)base.Data; } set { base.Data = value; } }

		public DirectJsonResult()
			: base(null, typeof(T))
		{

		}
		public DirectJsonResult(T data)
			: base(data, typeof(T))
		{

		}
	}
}
